package multithread.framework.utility.convert;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * Provide convert service from <tt>String</tt> to other data types
 * 
 * @author rico_yu
 * @since 2012-01-04
 */
@SuppressWarnings("unused")
public final class TypeConvert {
	private static final String CONVERT_METHOD = "convert2Target";
	private static final Log log = LogFactory.getLog(TypeConvert.class);
	private static final Class<TypeConvert> clazz = TypeConvert.class;
	private static final Map<Class<?>, Object> DEFAULT_VALUES = new HashMap<Class<?>, Object>();
	static {
		DEFAULT_VALUES.put(int.class, 0);
		DEFAULT_VALUES.put(long.class, 0l);
		DEFAULT_VALUES.put(double.class, 0d);
		DEFAULT_VALUES.put(float.class, 0f);
		DEFAULT_VALUES.put(boolean.class, true);
		DEFAULT_VALUES.put(char.class, 'c');
		DEFAULT_VALUES.put(short.class, Short.valueOf("0"));
		DEFAULT_VALUES.put(byte.class, Byte.valueOf("0"));
	}

	private TypeConvert() {
	}

	/**
	 * This is a entrance method provided to client. You just provided a
	 * <tt>Class</tt> object indicate which type you want to convert to, and
	 * it's related string value to be converted
	 * 
	 * @param <T>
	 * @param type
	 *            The target data type
	 * @param value
	 *            The String presentation
	 * @return The proper value with desired type
	 */
	@SuppressWarnings("unchecked")
	public static <T> T convert(Class<?> type, String value) {
		Class<?>[] paramTypes = new Class<?>[] { type, String.class };
		Object[] paramValues = new Object[] { DEFAULT_VALUES.get(type), value };
		Method method;
		try {
			method = clazz.getDeclaredMethod(CONVERT_METHOD, paramTypes);
		} catch (Exception e) {
			String message = "Error try to find method:\"convert2Target\" with parameter types: "
					+ type + ", java.lang.String";
			log.error(message, e);
			throw new RuntimeException(message, e);
		}
		try {
			return (T) method.invoke(null, paramValues);
		} catch (Exception e) {
			String message = "Error when invoke method:\"convert2Target\" with parameter types: "
					+ type + ", java.lang.String, parameter values: "
					+ DEFAULT_VALUES.get(type) + ", " + value;
			log.error(message, e);
			throw new RuntimeException(message, e);
		}
	}
	
	/**
	 * This is a entrance method provided to client. You just provided a
	 * <tt>Class</tt> object indicate which type of array you want to convert to, and
	 * it's related array of string value to be converted
	 * @param <T>
	 * @param type
	 * @param strArr
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static <T> T convert(Class<?> type, String[] strArr) {
		Class<?>[] paramTypes = new Class<?>[] { type, String[].class};
		Object[] paramValues = new Object[] { DEFAULT_VALUES.get(type), strArr };
		Method method;
		try {
			method = clazz.getDeclaredMethod(CONVERT_METHOD, paramTypes);
		} catch (Exception e) {
			String message = "Error try to find method:\"convert2Target\" with parameter types: "
					+ type + ", "+String[].class;
			log.error(message, e);
			throw new RuntimeException(message, e);
		}
		try {
			return (T) method.invoke(null, paramValues);
		} catch (Exception e) {
			String message = "Error when invoke method:\"convert2Target\" with parameter types: "
					+ type + ", java.lang.String[], parameter values: "
					+ DEFAULT_VALUES.get(type) + ", " + strArr;
			log.error(message, e);
			throw new RuntimeException(message, e);
		}

	}

	/**
	 * Convert parameter "value" to a int value.
	 * <p>
	 * Parameter type is just a mark to help "convert" method to find the proper
	 * "convert2Target" method
	 * 
	 * @param type
	 * @param value
	 * @return int
	 */
	private static int convert2Target(int type, String value) {
		return Integer.valueOf(value);
	}
	
	/**
	 * Convert a String array to int array.
	 * @param type
	 * @param strArr Should not be null
	 * @return int[]
	 */
	public static int[] convert2Target(int type, String[] strArr) {
		int[] intArr = new int[strArr.length];
		for (int i = 0; i < strArr.length; i++) {
			intArr[i] = convert2Target(type, strArr[i]);
		}
		return intArr;
	}

	private static long convert2Target(long type, String value) {
		return Long.valueOf(value);
	}
	public static long[] convert2Target(long type, String[] strArr) {
		long[] longArr = new long[strArr.length];
		for (int i = 0; i < strArr.length; i++) {
			longArr[i] = convert2Target(type, strArr[i]);
		}
		return longArr;
	}

	private static double convert2Target(double type, String value) {
		return Double.valueOf(value);
	}
	
	public static double[] convert2Target(double type, String[] strArr) {
		double[] doubleArr = new double[strArr.length];
		for (int i = 0; i < strArr.length; i++) {
			doubleArr[i] = convert2Target(type, strArr[i]);
		}
		return doubleArr;
	}

	private static float convert2Target(float type, String value) {
		return Float.valueOf(value);
	}
	
	public static float[] convert2Target(float type, String[] strArr) {
		float[] floatArr = new float[strArr.length];
		for (int i = 0; i < strArr.length; i++) {
			floatArr[i] = convert2Target(type, strArr[i]);
		}
		return floatArr;
	}

	private static boolean convert2Target(boolean type, String value) {
		return Boolean.parseBoolean(value);
	}
	
	public static boolean[] convert2Target(boolean type, String[] strArr) {
		boolean[] booleanArr = new boolean[strArr.length];
		for (int i = 0; i < strArr.length; i++) {
			booleanArr[i] = convert2Target(type, strArr[i]);
		}
		return booleanArr;
	}

	private static char convert2Target(char type, String value) {
		return value.charAt(0);
	}
	
	public static char[] convert2Target(char type, String[] strArr) {
		char[] charArr = new char[strArr.length];
		for (int i = 0; i < strArr.length; i++) {
			charArr[i] = convert2Target(type, strArr[i]);
		}
		return charArr;
	}

	private static short convert2Target(short type, String value) {
		return Short.parseShort(value);
	}
	
	public static short[] convert2Target(short type, String[] strArr) {
		short[] shortArr = new short[strArr.length];
		for (int i = 0; i < strArr.length; i++) {
			shortArr[i] = convert2Target(type, strArr[i]);
		}
		return shortArr;
	}

	private static byte convert2Target(byte type, String value) {
		return Byte.valueOf(value);
	}
	
	public static byte[] convert2Target(byte type, String[] strArr) {
		byte[] byteArr = new byte[strArr.length];
		for (int i = 0; i < strArr.length; i++) {
			byteArr[i] = convert2Target(type, strArr[i]);
		}
		return byteArr;
	}
	

}
